package com.wzh.gateway.listener;

import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wzh.common.general.exception.CustomerException;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.concurrent.Executor;

/**
 * <p>
 * 网关动态路由监听器，监听Nacos配置变化并实时更新路由规则
 * </p>
 *
 * @author 山归岿
 * @create 2025-06-15-19:42
 */
@Slf4j
@Configuration
public class DynamicRouteListener implements ApplicationEventPublisherAware, InitializingBean {
    private ApplicationEventPublisher publisher;
    private final RouteDefinitionWriter routeDefinitionWriter;
    private final NacosConfigManager nacosConfigManager;
    private final ObjectMapper objectMapper = new ObjectMapper();

    public DynamicRouteListener(RouteDefinitionWriter routeDefinitionWriter,
                                NacosConfigManager nacosConfigManager) {
        this.routeDefinitionWriter = routeDefinitionWriter;
        this.nacosConfigManager = nacosConfigManager;
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
        this.publisher = publisher;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        initNacosListener();
        loadInitialConfig(); // 确保启动时加载初始配置
    }

    private void initNacosListener() {
        try {
            ConfigService configService = nacosConfigManager.getConfigService();
            String dataId = "gateway-routes";
            String group = "DEFAULT_GROUP";

            // 添加监听器
            configService.addListener(dataId, group, new Listener() {
                @Override
                public void receiveConfigInfo(String configInfo) {
                    log.info("收到路由配置变更通知");
                    try {
                        refreshRoutes(configInfo);
                    } catch (JsonProcessingException e) {
                        log.error("路由配置解析失败", e);
                    }
                }

                @Override
                public Executor getExecutor() {
                    // 使用当前线程执行
                    return Runnable::run;
                }
            });

            log.info("已注册Nacos配置监听器: dataId={}, group={}", dataId, group);
        } catch (NacosException e) {
            log.error("Nacos监听器注册失败", e);
            throw new CustomerException("Nacos监听器初始化失败");
        }
    }

    private void loadInitialConfig() {
        try {
            ConfigService configService = nacosConfigManager.getConfigService();
            String dataId = "gateway-routes";
            String group = "DEFAULT_GROUP";

            // 获取初始配置
            String configInfo = configService.getConfig(dataId, group, 5000);
            if (configInfo != null && !configInfo.trim().isEmpty()) {
                log.info("加载初始路由配置");
                refreshRoutes(configInfo);
            } else {
                log.warn("未找到初始路由配置: dataId={}, group={}", dataId, group);
            }
        } catch (NacosException | JsonProcessingException e) {
            log.error("初始路由配置加载失败", e);
        }
    }

    private void refreshRoutes(String configInfo) throws JsonProcessingException {
        log.info("刷新路由配置:\n{}", configInfo);

        // 解析路由配置
        List<RouteDefinition> routes = parseRoutes(configInfo);
        if (routes == null || routes.isEmpty()) {
            log.warn("路由配置为空，跳过刷新");
            return;
        }

        // 添加新路由（Gateway会自动处理重复路由）
        for (RouteDefinition route : routes) {
            try {
                routeDefinitionWriter.save(Mono.just(route)).block();
                log.info("添加路由: ID={}, URI={}", route.getId(), route.getUri());
            } catch (Exception e) {
                log.error("添加路由失败: ID={}, 错误: {}", route.getId(), e.getMessage());
            }
        }

        // 发布路由刷新事件
        publisher.publishEvent(new RefreshRoutesEvent(this));
        log.info("已发布路由刷新事件");
    }

    private List<RouteDefinition> parseRoutes(String config) throws JsonProcessingException {
        try {
            // 尝试解析为JSON数组
            return objectMapper.readValue(config, new TypeReference<List<RouteDefinition>>() {
            });
        } catch (JsonProcessingException e) {
            // 尝试解析为包含routes属性的对象
            RouteConfigWrapper wrapper = objectMapper.readValue(config, RouteConfigWrapper.class);
            return wrapper.getRoutes();
        }
    }

    // 包装类用于解析包含routes属性的配置
    @Getter
    @Setter
    private static class RouteConfigWrapper {
        private List<RouteDefinition> routes;
    }
}